#include "service.h"

#include <unistd.h>

#include <iostream>
#include <cstring>
#include <string>

#include "service_msg.h"
#include "sunnet.h"
#include "common.h"
#include "socket_msg.h"
#include "lua_api.h"
#include "lua_log.h"
#include "packet_base.h"
#include "log_msg.h"


Service::Service(){
    pthread_spin_init(&queue_lock_, PTHREAD_PROCESS_PRIVATE);
    pthread_spin_init(&in_global_lock_, PTHREAD_PROCESS_PRIVATE);
}

Service::~Service(){
    pthread_spin_destroy(&queue_lock_);
    pthread_spin_destroy(&in_global_lock_);
}

void Service::SetInGlobal(bool in_global){
    pthread_spin_lock(&in_global_lock_);{
        in_global_ = in_global;
    }
    pthread_spin_unlock(&in_global_lock_);
}

std::shared_ptr<BaseMsg> Service::PopMsg(){
    std::shared_ptr<BaseMsg> msg = nullptr;
    pthread_spin_lock(&queue_lock_);
    {
        if(!msg_queue_.empty()){
            msg = msg_queue_.front();
            msg_queue_.pop();
        }
    }
    pthread_spin_unlock(&queue_lock_);

    return msg;
}

void Service::OnInit(){
    if(GetType()->compare(common::LOG_NAME) == 0){
        return;
    }

    LOG_INFO("Service OnInit:{}", id_);
    //新建Lua虚拟机
    lua_state_ = luaL_newstate();
    luaL_openlibs(lua_state_);

    //注册Sunnet系统API
    LuaAPI::Register(lua_state_);
    // 注册lua log
    LuaLog::Register(lua_state_);

    //执行Lua文件
    std::string filename = "../service/" + *type_ + "/init.lua";
    int isok = luaL_dofile(lua_state_, filename.data());
    if(isok == 1){ // 若成功则返回值为0，若失败则为1
        LOG_ERR("run lua fail:{}", lua_tostring(lua_state_, -1));
    }

    //调用Lua函数
    lua_getglobal(lua_state_, "OnInit");
    lua_pushinteger(lua_state_, id_);
    isok = lua_pcall(lua_state_, 1, 0, 0);
    if(isok != 0){ // 若返回值为0则代表成功，否则代表失败
        LOG_ERR("call lua OnInit fail {}", lua_tostring(lua_state_, -1));
    }
}

void Service::OnMsg(std::shared_ptr<BaseMsg> msg_base){
    BaseMsg::MsgType type = msg_base->GetType();
    if(type == BaseMsg::MsgType::SERVICE){
        auto msg = std::dynamic_pointer_cast<ServiceMsg>(msg_base);
        OnServiceMsg(msg);
    }else if(type == BaseMsg::MsgType::SOCKET_ACCEPT){
        auto msg = std::dynamic_pointer_cast<SocketAcceptMsg>(msg_base);
        OnAcceptMsg(msg);
    }else if(type == BaseMsg::MsgType::SOCKET_RW){
        auto msg = std::dynamic_pointer_cast<SocketRWMsg>(msg_base);
        OnRWMsg(msg);
    }else if(type == BaseMsg::MsgType::LOG){
        auto msg = std::dynamic_pointer_cast<LogMsg>(msg_base);
        OnLogMsg(msg);
    }else{
        LOG_WARN("no type msg:{}", static_cast<int>(type));
    }
}

void Service::OnExit(){
    LOG_INFO("Service OnExit:{}", id_);
    //调用Lua函数
    lua_getglobal(lua_state_, "OnExit");
    int isok = lua_pcall(lua_state_, 0, 0, 0);
    if(isok != 0){ // 若返回值为0则代表成功，否则代表失败
        LOG_ERR("call lua OnExit fail {}", lua_tostring(lua_state_, -1));
    }

    //关闭Lua虚拟机
    lua_close(lua_state_);
}

void Service::PushMsg(std::shared_ptr<BaseMsg> msg){
    pthread_spin_lock(&queue_lock_);
    {
        msg_queue_.push(msg);
    }
    pthread_spin_unlock(&queue_lock_);
}

bool Service::ProcessMsg(){
    std::shared_ptr<BaseMsg> msg = PopMsg();

    if(msg){
        OnMsg(msg);
        return true;
    }

    return false;
}

void Service::ProcessMsgs(int max){
    for(int i = 0; i < max; ++i){
        if(!ProcessMsg()){
            break;
        }
    }
}

void Service::OnServiceMsg(std::shared_ptr<ServiceMsg> msg){
    LOG_INFO("Service OnMsg:{} msg:{}", id_, msg->GetBuff().get());
}

void Service::OnAcceptMsg(std::shared_ptr<SocketAcceptMsg> msg){
    LOG_DEBUG("OnMsg type: BaseMsg::MsgType::SOCKET_ACCEPT service_id:{} cli_fd:{}", id_, msg->GetCliFd());
    auto conn = Sunnet::GetInstance().GetConn(msg->GetCliFd());
    if(conn == nullptr){
        return;
    }
    conn->GetReader()->RegisterCb(std::bind(&Service::OnPacketRead, this, std::placeholders::_1, std::placeholders::_2));
}

void Service::OnRWMsg(std::shared_ptr<SocketRWMsg> msg){
    LOG_DEBUG("OnMsg type: BaseMsg::MsgType::SOCKET_RW service_id:{} cli_fd:{}", id_, msg->GetFd());
    int fd = msg->GetFd();

    if(msg->IsRead()){
        auto conn = Sunnet::GetInstance().GetConn(fd);
        if(conn == nullptr){
            return;
        }
        
        int len = conn->GetReader()->Recv();
        if(len < 0 && (errno != EAGAIN)){
            if(Sunnet::GetInstance().GetConn(fd)){
                OnSocketClose(fd);
                Sunnet::GetInstance().CloseConn(fd);
            }
        }
    }

    if(msg->IsWrite()){
        if(Sunnet::GetInstance().GetConn(fd)){
            OnSocketWritable(fd);
        }
    }

    if(msg->IsClose()){
        if(Sunnet::GetInstance().GetConn(fd)){
            OnSocketClose(fd);
            Sunnet::GetInstance().CloseConn(fd);
        }
    }
}

void Service::OnSocketData(int fd, const char* buff, int len){
    LOG_DEBUG("OnSocketData msg: {}", buff);

    //调用Lua函数
    lua_getglobal(lua_state_, "OnSocketData");
    lua_pushinteger(lua_state_, fd);
    lua_pushinteger(lua_state_, len);
    lua_pushstring(lua_state_, buff);
    int isok = lua_pcall(lua_state_, 3, 0, 0);
    if(isok != 0){ // 若返回值为0则代表成功，否则代表失败
        LOG_ERR("call lua OnSocketData fail {}", lua_tostring(lua_state_, -1));
    }
}

void Service::OnSocketWritable(int fd){
    LOG_DEBUG("OnSocketWritable fd:{}", fd);

    auto conn = Sunnet::GetInstance().GetConn(fd);
    if(conn){
        conn->GetWriter()->OnWriteable();
    }
}

void Service::OnSocketClose(int fd){
    LOG_INFO("OnSocketClose, fd:{}", fd);
}

void Service::OnPacketRead(int fd, PacketBase& packet){
    uint16_t len = packet.GetBodySize() + common::PROTO_HEADER_LEN;
    char buff[len];
    memcpy(buff, packet.GetOriginBuffer(), len);
    //调用Lua函数
    lua_getglobal(lua_state_, "OnPacketData");
    lua_pushinteger(lua_state_, fd);
    lua_pushinteger(lua_state_, len);
    lua_pushlightuserdata(lua_state_, buff);
    int isok = lua_pcall(lua_state_, 3, 0, 0);
    if(isok != 0){ // 若返回值为0则代表成功，否则代表失败
        LOG_ERR("call lua OnPacketData fail {}", lua_tostring(lua_state_, -1));
    }
}

void Service::OnLogMsg(std::shared_ptr<LogMsg> msg){
    LogMsg::EType type = msg->GetLogType();
    if(type == LogMsg::EType::EINFO){
        LOG_INFO(msg->GetBuff().get());
    }else if(type == LogMsg::EType::EDEBUG){
        LOG_DEBUG(msg->GetBuff().get());
    }else if(type == LogMsg::EType::EWARN){
        LOG_WARN(msg->GetBuff().get());
    }else if(type == LogMsg::EType::EERROR){
        LOG_ERR(msg->GetBuff().get());
    }else{
        LOG_DEBUG("ERROR LOG TYPE");
    }
}